시스템 호출
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
시스템 호출은 운영 체제가 제공하는 서비스에 접근하기 위해, 사용자 프로그램이 커널에 요청하는 인터페이스이다. 대부분의 현대 아키텍처는 보안을 위해 CPU의 권한 수준을 구분하며, 시스템 호출은 프로그램이 하드웨어에 직접 접근하는 것을 제한하고, 운영 체제가 안전하게 자원을 관리하도록 한다. 시스템 호출은 인터럽트를 통해 시작되며, 커널은 요청된 서비스를 수행하고 사용자 프로그램으로 제어를 반환한다. 시스템 호출은 프로세스 제어, 파일 관리, 장치 관리, 정보 유지 관리, 통신, 보호 등 다양한 유형으로 분류되며, 리눅스와 같은 운영 체제에서는 수백 가지의 시스템 호출을 제공한다. 이러한 시스템 호출은 래퍼 함수나 라이브러리를 통해 사용되며, 시스템 호출의 작동 방식은 하드웨어 아키텍처에 따라 다르다.
더 읽어볼만한 페이지
- 시스템 호출 - 포크 (시스템 호출)
포크는 유닉스 계열 운영체제에서 새로운 프로세스를 생성하는 시스템 호출로, 호출 시 부모 프로세스의 복사본인 자식 프로세스가 생성되어 파일 서술자를 상속받으며, 유닉스 철학의 핵심 개념으로 필터 개발 및 파이프라인 구축에 용이하고 vfork, rfork, clone 등 다양한 변종이 존재한다. - 시스템 호출 - Chroot
`chroot`는 유닉스 계열 운영체제에서 프로세스의 루트 디렉토리를 변경하는 시스템 호출 및 환경으로, 다양한 목적으로 사용되지만 보안 취약점과 제한 사항을 가지며, `fakechroot`는 root 권한 없이 유사한 환경을 제공한다. - API - Tk (소프트웨어)
Tk는 Tcl 스크립팅 언어의 크로스 플랫폼 GUI 툴킷으로, 다양한 플랫폼 이식과 여러 프로그래밍 언어 바인딩을 지원하며 사용자 정의 가능한 위젯들을 제공한다. - API - ASIO
ASIO는 독일 스타인버그에서 개발한 오디오 입출력 API 규격으로, 낮은 지연 시간과 멀티 채널 I/O를 지원하며 윈도우 운영체제에서 주로 사용된다. - 운영체제 기술 - 프로세스
프로세스는 컴퓨터에서 실행되는 프로그램의 인스턴스로, 운영 체제가 시스템 자원을 효율적으로 관리하며 멀티태스킹 환경에서 독립적인 실행 흐름을 유지한다. - 운영체제 기술 - 커널 (컴퓨팅)
커널은 운영 체제의 핵심으로, 하드웨어와 소프트웨어 간 상호 작용을 관리하며 시스템 보안, 자원 관리, 하드웨어 추상화, 프로세스 스케줄링, 프로세스 간 통신, 다중 작업 환경 지원 등의 기능을 제공하고, 모놀리식, 마이크로, 혼합형 커널 등으로 구현되며 가상화 및 클라우드 컴퓨팅 환경에서 중요성이 커지고 있다.
시스템 호출 | |
---|---|
기본 정보 | |
유형 | 추상화 |
하위 클래스 | 운영 체제 컴퓨터 아키텍처 |
구현 | 운영 체제 커널 |
사용 | API 라이브러리 |
시스템 호출 | |
설명 | 운영 체제의 커널이 제공하는 서비스에 대한 프로세스의 요청 |
용도 | 사용자 공간 프로세스가 커널 공간의 서비스에 접근하기 위한 유일한 방법 |
작동 방식 | 사용자 프로세스가 시스템 호출을 요청 운영 체제는 요청을 받아 처리 결과 또는 오류 코드를 사용자 프로세스에 반환 |
관련된 것 | 운영 체제 |
예시 | |
예시 | 파일 입출력 네트워킹 프로세스 생성 및 관리 |
관련 용어 | |
관련 용어 | API (응용 프로그램 프로그래밍 인터페이스) 라이브러리 커널 운영 체제 프로세스 |
2. 권한 (Privileges)
대부분의 현대 운영 체제는 보안 모델을 기반으로 하며, CPU 모드에 따라 권한 수준을 달리한다.
많은 애플리케이션이 프레임 버퍼나 네트워크 장치, 또는 운영체제 자체에 접근해야 하므로, 운영 체제는 이러한 작업에 대해 잘 정의되고 안전한 구현을 제공하기 위해 시스템 호출을 사용할 수 있도록 한다. 운영 체제는 최고 수준의 권한으로 실행되며 애플리케이션이 시스템 호출을 통해 서비스를 요청할 수 있도록 한다. 시스템 호출은 종종 인터럽트를 통해 시작된다. 인터럽트는 자동으로 CPU를 높은 권한 수준으로 설정한 다음 커널로 제어 권한을 넘긴다. 커널은 호출 프로그램에 요청된 서비스를 부여해야 하는지 여부를 결정한다. 서비스가 부여되면 커널은 호출 프로그램이 직접 제어할 수 없는 특정 명령어 집합을 실행하고, 권한 수준을 호출 프로그램의 수준으로 되돌린 다음 호출 프로그램으로 제어 권한을 반환한다.[1]
현대의 프로세서는 일반적으로 몇 가지 특권 상태에서 명령어를 실행한다. 두 가지 레벨을 가진 시스템에서는 이것을 일반적으로 사용자 모드와 슈퍼바이저 모드라고 부른다. 운영 체제의 커널은 슈퍼바이저 모드에서 동작하고, 사용자 애플리케이션은 사용자 모드에서 동작한다.
2. 1. 링(Ring) 모델
링 모델은 소프트웨어가 실행될 수 있는 여러 권한 수준을 지정한다. 일반적으로 프로그램은 자체 주소 공간으로 제한되어 다른 실행 중인 프로그램이나 운영 체제 자체에 접근하거나 수정할 수 없으며, 일반적으로 하드웨어 장치(예: 프레임 버퍼 또는 네트워크 장치)를 직접 조작하는 것이 금지된다.현대 프로세서는 일반적으로 몇 가지 특권 상태에서 명령어를 실행한다. 두 개의 레벨을 가진 시스템에서는 이것을 일반적으로 사용자 모드와 슈퍼바이저 모드라고 부른다. 이러한 특권 레벨이 있는 이유는 보안과 안정성을 유지하기 위해 운영 체제가 그 관리 하에 동작하는 프로그램에 의한 조작을 제한할 수 있도록 하기 위함이다. 그러한 제한을 받는 조작으로는 하드웨어 장치에 대한 접근, 인터럽트의 허용/불허 변경, 프로세서의 특권 상태 변경, 메모리 관리 장치에 대한 접근 등이 있다. 운영 체제의 커널은 슈퍼바이저 모드에서 동작하고, 사용자 애플리케이션은 사용자 모드에서 동작한다.[1]
복수의 특권 레벨을 가진 시스템을 개발할 때, 낮은 특권 레벨로부터 높은 특권 레벨로 제어를 안전하게 전송하는 방법이 필요하다. 낮은 특권 레벨의 코드가 단순히 높은 특권 레벨로 이행해서는 보안과 안정성을 유지할 수 없다. 예를 들어, 낮은 특권 레벨의 코드가 높은 특권 레벨의 코드에 잘못된 처리를 시키거나, 부정한 콜 스택을 넘겨줄 수도 있다.[1]
2. 2. 시스템 호출의 필요성
응용 프로그램이 하드웨어나 운영 체제의 기능에 접근하려면 시스템 호출을 사용해야 한다. 운영 체제는 최고 수준의 권한으로 실행되며, 시스템 호출을 통해 응용 프로그램의 요청을 처리한다.[1]리눅스의 커널은 최고 권한 수준(x86, Ring 0)에서 실행되며, 하드웨어 제어, 응용 프로그램 스케줄링 및 시분할 실행을 제어한다.[1] 하드웨어 제어 권한은 커널이 가지고 있으므로, 파일 시스템 같은 경우 응용 프로그램에서 직접 제어할 수 없다.[1] 따라서 응용 프로그램이 하드웨어의 데이터를 가져오거나 쓰려면 커널의 장치 드라이버와 연동되어 실행되어야 한다.[1] 결국 응용 프로그램이 파일 시스템을 이용하려면 커널의 파일 시스템 드라이버로 넘어가 실행되어야 하므로 시스템 호출 방법을 사용한다.[1]
많은 현대 아키텍처는 보안 모델을 포함한다. (일부 임베디드 시스템 제외)[1] 예를 들어, ''링'' 모델은 소프트웨어가 실행될 수 있는 여러 권한 수준을 지정한다.[1] 일반적으로 프로그램은 자체 주소 공간으로 제한되어 다른 실행 중인 프로그램이나 운영 체제 자체에 접근하거나 수정할 수 없으며, 하드웨어 장치(예: 프레임 버퍼 또는 네트워크 장치)를 직접 조작하는 것이 금지된다.[1]
그러나 많은 애플리케이션이 이러한 구성 요소에 접근해야 하므로, 운영 체제는 이러한 작업에 대해 잘 정의되고 안전한 구현을 제공하기 위해 시스템 호출을 사용할 수 있도록 한다.[1] 운영 체제는 최고 수준의 권한으로 실행되며 애플리케이션이 시스템 호출을 통해 서비스를 요청할 수 있도록 한다.[1] 시스템 호출은 종종 인터럽트를 통해 시작된다.[1] 인터럽트는 자동으로 CPU를 높은 권한 수준으로 설정한 다음 커널로 제어 권한을 넘긴다.[1] 커널은 호출 프로그램에 요청된 서비스를 부여해야 하는지 여부를 결정한다.[1] 서비스가 부여되면 커널은 호출 프로그램이 직접 제어할 수 없는 특정 명령어 집합을 실행하고, 권한 수준을 호출 프로그램의 수준으로 되돌린 다음 호출 프로그램으로 제어 권한을 반환한다.[1]
2. 3. 시스템 호출의 작동 방식
일반적으로 시스템 호출은 인터럽트를 통해 시작된다. 인터럽트는 자동적으로 CPU를 높은 권한 수준으로 설정하고, 제어 권한을 커널로 넘긴다. 커널은 호출 프로그램이 요청한 서비스를 제공할지 여부를 결정한다. 서비스가 허용되면 커널은 호출 프로그램이 직접 제어할 수 없는 특정 명령어 집합을 실행하고, 권한 수준을 호출 프로그램의 수준으로 되돌린 다음 호출 프로그램으로 제어 권한을 반환한다.현대의 프로세서는 보통 여러 특권 상태에서 명령어를 실행한다. 두 가지 레벨을 가진 시스템에서는 이를 사용자 모드와 슈퍼바이저 모드라고 부른다. 이러한 특권 레벨이 있는 이유는 보안과 안정성을 유지하기 위해 운영 체제가 그 관리 하에 동작하는 프로그램에 의한 조작을 제한할 수 있도록 하기 위함이다. 제한받는 조작에는 하드웨어 장치 접근, 인터럽트 허용/불허 변경, 프로세서 특권 상태 변경, 메모리 관리 장치 접근 등이 있다.[1] 운영 체제의 커널은 슈퍼바이저 모드에서 동작하고, 사용자 애플리케이션은 사용자 모드에서 동작한다.[1]
낮은 특권 레벨에서 높은 특권 레벨로 안전하게 제어를 전송하는 방법이 필요하다. 낮은 특권 레벨의 코드가 단순히 높은 특권 레벨로 이행해서는 보안과 안정성을 유지할 수 없다. 예를 들어 낮은 특권 레벨의 코드가 높은 특권 레벨의 코드에 잘못된 처리를 시키거나, 부정한 콜 스택을 넘겨줄 수도 있기 때문이다.[1]
시스템 콜은 전용 명령어(인텔 x86는 펜티엄 II 이후, ARM은 처음부터) 혹은 소프트웨어 인터럽트에 의해 실행된다. CPU의 동작 모드를 전환함으로써 일반적인 애플리케이션 프로그램에서는 접근할 수 없는 보호된 메모리 영역에 접근하거나, 보호된 레지스터를 조작하거나, 스스로 CPU의 동작 모드를 변경하는 것 등이 가능해진다.[2]
시스템 콜은 특수한 명령어를 사용하는 경우가 많으며, 이를 통해 CPU는 높은 특권 레벨의 코드로 제어를 넘긴다. 구체적인 방법은 시스템에 따라 다르지만, 예외나 인터럽트를 발생시켜 높은 특권 레벨로 전환하거나, 특수한 분기 명령어로 높은 특권 레벨로 전환한다. 이때 시스템 콜의 종류를 나타내는 번호나 인수가 레지스터나 콜 스택에 저장되어 높은 특권 레벨의 코드(커널)가 이를 사용하여 처리를 수행한다.[3]
시스템 콜이 호출되면 호출한 프로그램은 중단되고, 나중에 처리를 계속하기 위해 필요한 정보(컨텍스트)가 저장된다. 그리고 프로세서는 높은 특권 레벨의 코드를 실행하여, 앞서 언급한 시스템 콜의 번호와 인수를 확인하고, 필요한 처리를 수행한다. 이 과정에서 호출 측의 접근 권한 등도 고려하여 지정된 시스템 콜을 실행할 권한이 있는지 여부를 확인한다. 완료되면 호출 측 프로그램으로 복귀하기 위해 저장되었던 상태 정보를 복원하고, 프로그램의 처리가 계속된다. 이때 소정의 레지스터(또는 스택의 소정 위치)에 반환값이 설정된다.[4]
많은 경우 프로그램으로의 복귀가 즉시 이루어지지 않을 수 있다. 시스템 콜에서는 시간이 오래 걸리는 I/O 처리를 할 수 있으며(예: 디스크나 네트워크에의 접근), 프로그램은 중단되어("블록" 상태), 해당 처리가 완료될 때까지 "실행 가능" 큐에 놓인다. 필요한 처리가 완료되면 운영 체제는 이를 실행 후보로 취급한다.[5]
3. 라이브러리의 역할
일반적으로 운영 체제는 사용자 프로그램과 운영 체제 사이에서 중개자 역할을 하는 라이브러리 또는 API를 제공한다. 이러한 라이브러리는 시스템 호출을 추상화하여 이식성을 향상시킨다.
유닉스 계열 시스템에서 API는 보통 C 라이브러리(libc)의 일부로, 시스템 호출과 같은 이름의 래퍼 함수를 포함한다. Windows NT에서는 ntdll.dll영어 라이브러리의 네이티브 API가 이 역할을 담당하며, 일반적인 Windows API 구현 및 일부 시스템 프로그램에서 사용된다.
라이브러리 래퍼 함수는 인수를 프로세서 레지스터나 호출 스택에 배치하고 커널 호출을 위한 시스템 호출 번호를 설정한다. 라이브러리 함수 호출 자체는 커널 모드로 전환되지 않지만, 실제 시스템 호출은 커널로 제어를 전송한다.
엑소커널 기반 시스템에서 라이브러리는 매우 낮은 수준의 커널 API로부터 사용자 애플리케이션을 보호하고 추상화 및 리소스 관리를 제공하여 중개자 역할을 수행한다.
3. 1. 유닉스 계열 시스템
일반적으로 유닉스 계열 시스템에서 해당 API는 C 라이브러리(libc) 구현의 일부이며, 여기에는 시스템 호출을 위한 래퍼 함수가 포함되어 있고, 이는 호출하는 시스템 호출과 동일한 이름을 갖는 경우가 많다. 라이브러리의 래퍼 함수는 시스템 호출을 사용하기 위해 일반적인 함수 호출 규칙(예: 어셈블리 레벨의 서브루틴 호출)을 노출할 뿐만 아니라 시스템 호출의 모듈성을 향상시킨다. 여기서 래퍼의 주요 기능은 시스템 호출에 전달될 모든 인수를 적절한 프로세서 레지스터에 배치하고 (그리고 호출 스택에도 배치할 수 있음) 커널이 호출할 고유한 시스템 호출 번호를 설정하는 것이다. 이러한 방식으로 OS와 애플리케이션 사이에 존재하는 라이브러리는 이식성을 향상시킨다.라이브러리 함수 자체에 대한 호출은 커널 모드로의 전환을 유발하지 않으며 일반적으로 일반적인 서브루틴 호출이다 (예: 일부 명령 집합 아키텍처(ISA)에서 "CALL" 어셈블리 명령 사용). 실제 시스템 호출은 제어를 커널로 전송하며, 이는 라이브러리 호출을 추상화하는 것보다 구현 종속적이고 플랫폼 종속적이다. 예를 들어, 유닉스 계열 시스템에서 `fork` 및 `execve`는 차례로 `fork` 및 `exec` 시스템 호출을 호출하는 명령을 실행하는 C 라이브러리 함수이다. 애플리케이션 코드에서 직접 시스템 호출을 수행하는 것은 더 복잡하며 ( C 및 C++에서) 임베디드 어셈블리 코드를 사용해야 할 수 있으며, 시스템 호출 작업에 대한 하위 수준 바이너리 인터페이스에 대한 지식이 필요하며 시간이 지남에 따라 변경될 수 있으므로 애플리케이션 바이너리 인터페이스의 일부가 아닐 수 있다. 라이브러리 함수는 이를 추상화하기 위한 것이다.
엑소커널 기반 시스템에서 라이브러리는 특히 중개자로서 중요하며, 엑소커널에서 라이브러리는 사용자 애플리케이션을 매우 낮은 수준의 커널 API로부터 보호하고 추상화 및 리소스 관리를 제공한다.
3. 2. Windows NT
Windows NT API는 ntdll.dll영어 라이브러리에 있는 네이티브 API의 일부이다. 이는 일반적인 Windows API 구현에서 사용되며, Windows의 일부 시스템 프로그램에서 직접 사용되는 문서화되지 않은 API이다. 라이브러리의 래퍼 함수는 시스템 호출을 사용하기 위해 일반적인 함수 호출 규칙(예: 어셈블리 레벨의 서브루틴 호출)을 노출할 뿐만 아니라 시스템 호출의 모듈성을 향상시킨다. 여기서 래퍼의 주요 기능은 시스템 호출에 전달될 모든 인수를 적절한 프로세서 레지스터에 배치하고 (호출 스택에도 배치할 수 있음) 커널이 호출할 고유한 시스템 호출 번호를 설정하는 것이다. 이러한 방식으로 OS와 애플리케이션 사이에 존재하는 라이브러리는 이식성을 향상시킨다.3. 3. 라이브러리의 기능
일반적으로 시스템은 일반 프로그램과 운영 체제 사이에 위치하는 라이브러리 또는 API를 제공한다. 유닉스 계열 시스템에서 해당 API는 일반적으로 C 라이브러리 (libc) 구현의 일부이며, 여기에는 시스템 호출을 위한 래퍼 함수가 포함되어 있고, 이는 호출하는 시스템 호출과 동일한 이름을 갖는 경우가 많다. Windows NT에서 해당 API는 ntdll.dll 라이브러리에 있는 네이티브 API의 일부인데, 이는 일반적인 Windows API 구현에서 사용되며 Windows의 일부 시스템 프로그램에서 직접 사용되는 문서화되지 않은 API이다. 라이브러리의 래퍼 함수는 시스템 호출을 사용하기 위해 일반적인 함수 호출 규칙 (예: 어셈블리 레벨의 서브루틴 호출)을 노출할 뿐만 아니라 시스템 호출의 모듈성을 향상시킨다. 여기서 래퍼의 주요 기능은 시스템 호출에 전달될 모든 인수를 적절한 프로세서 레지스터에 배치하고 (그리고 호출 스택에도 배치할 수 있음) 커널이 호출할 고유한 시스템 호출 번호를 설정하는 것이다. 이러한 방식으로 OS와 애플리케이션 사이에 존재하는 라이브러리는 이식성을 향상시킨다.라이브러리 함수 자체에 대한 호출은 커널 모드로의 전환을 유발하지 않으며 일반적으로 일반적인 서브루틴 호출이다 (예: 일부 명령 집합 아키텍처 (ISA)에서 "CALL" 어셈블리 명령 사용).[1] 실제 시스템 호출은 제어를 커널로 전송하며 (라이브러리 호출을 추상화하는 것보다 구현 종속적이고 플랫폼 종속적이다).[1] 예를 들어, 유닉스 계열 시스템에서 `fork` 및 `execve`는 차례로 `fork` 및 `exec` 시스템 호출을 호출하는 명령을 실행하는 C 라이브러리 함수이다.[1] 애플리케이션 코드에서 직접 시스템 호출을 수행하는 것은 더 복잡하며 ( C 및 C++에서) 임베디드 어셈블리 코드를 사용해야 할 수 있으며, 시스템 호출 작업에 대한 하위 수준 바이너리 인터페이스에 대한 지식이 필요하며 시간이 지남에 따라 변경될 수 있으므로 애플리케이션 바이너리 인터페이스의 일부가 아닐 수 있다.[1] 라이브러리 함수는 이를 추상화하기 위한 것이다.[1]
엑소커널 기반 시스템에서 라이브러리는 특히 중개자로서 중요하다.[2] 엑소커널에서 라이브러리는 사용자 애플리케이션을 매우 낮은 수준의 커널 API로부터 보호하고 추상화 및 리소스 관리를 제공한다.[2]
일반적으로 운영 체제는 사용자 프로그램과 운영 체제의 중간에 위치하는 라이브러리를 제공하고, 표준 C 라이브러리를 구현한 것(또는 동등한 기능을 가진 것)이 많다.[7] 이 라이브러리 내에서 실제 시스템 콜(커널에 전달하는 정보의 설정이나 특권 모드로의 전환)이 이루어지거나, 특권 레벨의 처리를 필요로 하지 않는 다양한 데이터 처리가 이루어진다. 이를 통해 운영 체제와 애플리케이션의 연결이 느슨해져 애플리케이션의 이식성이 높아졌다고 할 수 있다. 특히 동적 링크 라이브러리라면 시스템 콜 처리 부분이 실행 시에 링크되기 때문에 애플리케이션 실행 파일을 그대로 다른 운영 체제에서 실행할 가능성이 높아진다.[8]
Exokernel을 기반으로 하는 시스템에서는 라이브러리가 특히 중요하다. Exokernel은 매우 낮은 수준의 커널 API만 제공하며, LibOS라고 불리는 라이브러리가 추상화 및 리소스 관리 기능을 제공하고 있다.[8]
3. 4. 엑소커널(Exokernel)
엑소커널 기반 시스템에서 라이브러리는 특히 중요한 역할을 수행한다. 엑소커널은 매우 낮은 수준의 커널 API만을 제공하기 때문에, 사용자 애플리케이션을 보호하고 추상화 및 리소스 관리를 제공하는 것은 라이브러리의 몫이다. LibOS는 이러한 라이브러리의 일종으로, 추상화 및 리소스 관리 기능을 제공한다.[1]4. 시스템 호출의 구현
시스템 호출을 구현하려면 사용자 공간에서 커널 공간으로 제어 권한을 이전해야 하며, 여기에는 아키텍처 관련 기능이 필요하다.[8]
시스템 호출이 호출되면 호출한 프로그램은 중단되고, 나중에 처리를 계속하기 위해 필요한 정보(컨텍스트)가 저장된다. 프로세서는 높은 특권 레벨의 코드를 실행하여 시스템 콜의 번호와 인수를 확인하고 필요한 처리를 수행한다. 이 과정에서 호출 측의 접근 권한 등을 고려하여 지정된 시스템 콜을 실행할 권한이 있는지 확인한다. 완료되면 호출 측 프로그램으로 복귀하기 위해 저장되었던 상태 정보를 복원하고, 프로그램의 처리가 계속된다. 이때 소정의 레지스터나 스택의 소정 위치에 반환값이 설정된다.
시스템 콜에서는 시간이 오래 걸리는 입출력(I/O) 처리를 할 수 있다(예: 디스크나 네트워크 접근). 이때 프로그램은 중단(블록)되어 해당 처리가 완료될 때까지 실행 가능 큐에 놓인다. 필요한 처리가 완료되면 운영 체제는 이를 실행 후보로 취급한다.
4. 1. 일반적인 구현 방법
소프트웨어 인터럽트 또는 트랩을 사용하는 것이 일반적인 시스템 호출 구현 방법이다.[8] 인터럽트는 제어 권한을 운영 체제 커널로 이전시키므로, 소프트웨어는 필요한 시스템 호출 번호를 레지스터에 설정하고 소프트웨어 인터럽트를 실행한다.[8]이는 많은 RISC 프로세서에서 제공되는 유일한 기술이지만, x86과 같은 CISC 아키텍처는 추가적인 기술을 지원한다. 예를 들어, x86 명령어 집합에는 `SYSCALL`/`SYSRET` 및 `SYSENTER`/`SYSEXIT` 명령어가 있다. 이 명령어들은 인터럽트 오버헤드 없이 시스템 호출을 위해 커널로 제어 권한을 빠르게 이전하도록 설계된 "빠른" 제어 전송 명령어이다.[8] AMD와 인텔이 각각 독립적으로 만들었지만, 본질적으로 동일한 작업을 수행한다.[8] 리눅스 2.5는 x86에서 이를 사용하기 시작했다. 이전에는 시스템 호출 번호가 `EAX` 레지스터에 배치된
INT
명령어를 사용한 다음 interrupt 0x80을 실행했다.[9][10]Multics에서 처음 사용되었고 나중에 인텔 x86에서 지원된 더 오래된 메커니즘으로는 호출 게이트가 있다. 이 방식을 통해 프로그램은 운영 체제가 미리 설정한 안전한 제어 전송 메커니즘을 사용하여 커널 함수를 직접 호출할 수 있었다. 하지만 x86에서는 널리 사용되지 않았는데, 먼 호출(현재 코드 세그먼트와 다른 세그먼트에 있는 프로시저를 호출하는 것[11])이 필요하고 x86 메모리 세분화를 사용하며, 그로 인해 이식성이 부족해지고, 위에 언급된 더 빠른 명령어가 존재했기 때문이다.
IA-64 아키텍처의 경우, `EPC` (Enter Privileged Code) 명령어가 사용된다. 처음 8개의 시스템 호출 인수는 레지스터로 전달되고 나머지는 스택으로 전달된다.
IBM System/360 메인프레임 제품군 및 후속 제품에서는 레지스터가 아닌 명령어에 번호가 있는 슈퍼바이저 호출 명령어를 사용하여 대부분의 IBM 자체 운영 체제에서 레거시 기능을 위한 시스템 호출을 구현하고, 리눅스에서는 모든 시스템 호출을 구현한다.
PDP-11 미니컴퓨터는 `EMT`, `TRAP` 및 `IOT` 명령어를 사용했다. 이는 IBM System/360 `SVC` 및 x86 `INT`와 유사하게 코드를 명령어에 넣어 특정 주소로 인터럽트를 생성하여 운영 체제로 제어 권한을 이전한다. PDP-11 시리즈의 32비트 후속 제품인 VAX는 `CHMK`, `CHME` 및 `CHMS` 명령어를 사용하여 다양한 수준의 권한 코드에 시스템 호출을 수행했으며, 코드는 명령어에 대한 인수였다.
시스템 콜은 전용 명령어(인텔 x86는 펜티엄 II 이후, ARM은 처음부터) 혹은 소프트웨어 인터럽트에 의해 실행된다. CPU의 동작 모드를 전환함으로써 일반적인 애플리케이션 프로그램에서는 접근할 수 없는 보호된 메모리 영역에 접근하거나, 보호된 레지스터를 조작하거나, 스스로 CPU의 동작 모드를 변경하는 것이 가능해진다.
시스템 콜은 특수한 명령어를 사용하는 경우가 많으며, 이를 통해 CPU는 높은 특권 레벨의 코드로 제어를 넘긴다. 구체적인 방법은 시스템에 따라 다르지만, 예외나 인터럽트를 발생시켜 높은 특권 레벨로 전환하거나, 특수한 분기 명령어로 높은 특권 레벨로 전환한다. 이때 시스템 콜의 종류를 나타내는 번호나 인수가 레지스터나 콜 스택에 저장되어 높은 특권 레벨의 코드(커널)가 이를 사용하여 처리를 수행한다.
4. 2. x86 아키텍처의 추가 기술
`SYSCALL`/`SYSRET` 및 `SYSENTER`/`SYSEXIT` 명령어는 AMD와 인텔(Intel)에서 각각 독립적으로 만들었지만, 본질적으로 동일한 기능을 수행하는 "빠른" 제어 전송 명령어이다. 이 명령어들은 인터럽트 오버헤드 없이 시스템 호출을 위해 커널로 제어 권한을 빠르게 이전하도록 설계되었다.[8] 리눅스 2.5는 x86에서 이 명령어들을 사용하기 시작했다. 이전에는 시스템 호출 번호를 `EAX` 레지스터에 배치한 후 `INT` 명령어와 interrupt 0x80을 실행했다.[9][10]4. 3. 호출 게이트(Call Gate)
Multics에서 처음 사용되었고, 인텔 x86에서도 호출 게이트를 지원한다. 이를 통해 프로그램은 운영 체제가 미리 설정한 안전한 제어 전송 메커니즘을 사용하여 커널 함수를 직접 호출할 수 있다.[11] 이 방식은 x86에서 인기가 없었는데, 이는 먼 호출(현재 코드 세그먼트와 다른 세그먼트에 있는 프로시저를 호출하는 것)이 필요하고 x86 메모리 세분화를 사용하며, 그로 인해 이식성이 부족해지고, 위에 언급된 더 빠른 명령어가 존재하기 때문으로 추정된다.4. 4. IA-64 아키텍처
IA-64 아키텍처에서는 EPC (Enter Privileged Code) 명령어가 사용된다.[8] 처음 8개의 시스템 호출 인수는 레지스터로 전달되고 나머지는 스택으로 전달된다.4. 5. IBM System/360 및 후속 제품
IBM System/360 메인프레임 제품군 및 그 후속 제품에서, 레지스터가 아닌 명령어에 번호를 가진 슈퍼바이저 호출 명령어(SVC)는 대부분의 IBM 자체 운영 체제에서 레거시 기능을 위한 시스템 호출을 구현하고, 리눅스에서는 모든 시스템 호출을 구현한다.[8] 최신 MVS 버전에서는 IBM이 프로그램 호출(PC) 명령어를 사용하여 많은 새로운 기능을 구현한다. 특히, 호출자가 서비스 요청 블록(SRB) 모드에 있을 수 있는 경우 PC가 사용된다.4. 6. PDP-11
PDP-11 미니컴퓨터는 , , 명령어를 사용했다. 이는 IBM System/360 및 x86 와 유사하게, 코드를 명령어에 넣어 특정 주소로 인터럽트를 생성하여 운영 체제로 제어 권한을 이전한다.[8] PDP-11 시리즈의 32비트 후속 제품인 VAX는 , , 명령어를 사용하여 다양한 수준의 권한 코드에 시스템 호출을 수행했으며, 코드는 명령어에 대한 인수였다.4. 7. VAX
PDP-11 미니컴퓨터의 32비트 후속 제품인 VAX는 `CHMK`, `CHME`, `CHMS` 명령어를 사용하여 시스템 호출을 수행했다. 이 명령어들은 코드(인수)를 통해 다양한 수준의 권한을 가진 코드로 시스템 호출을 가능하게 했다.[8]5. 시스템 호출의 유형
시스템 호출은 크게 다음 6가지 주요 범주로 그룹화할 수 있다.[12]
범주 | 설명 |
---|---|
프로세스 제어 | 프로세스 생성, 프로세스 종료, 로드, 실행 등을 포함한다. |
파일 관리 | 파일 생성 및 삭제, 열기 및 닫기, 읽기, 쓰기 등을 포함한다. |
장치 관리 | 장치 요청 및 해제, 읽기, 쓰기, 장치 속성 설정 등을 포함한다. |
정보 유지 관리 | 시스템 정보, 프로세스, 파일 또는 장치 메타데이터 가져오기/설정 등을 포함한다. |
통신 | 통신 연결 생성 및 삭제, 메시지 송수신, 상태 정보 전송 등을 포함한다. |
보호 | 파일 권한 가져오기/설정을 포함한다. |
5. 1. 프로세스 제어
- 프로세스 생성 (예: 유닉스 계열 시스템의
fork
또는 Windows NT 네이티브 API의NtCreateProcess
)[12] - 프로세스 종료[12]
- 로드, 실행[12]
- 프로세스 속성 가져오기/설정[12]
- 시간 대기, 이벤트 대기, 시그널 이벤트 대기[12]
- 메모리 할당 및 해제[12]
5. 2. 파일 관리
POSIX 및 유사 시스템에서 주요 시스템 콜로는 open, read, write, close 등이 있다.[12]
5. 3. 장치 관리
5. 4. 정보 유지 관리
5. 5. 통신
5. 6. 보호
파일 권한을 가져오고 설정한다.[12]6. 명령어 실행과 시스템 호출 (리눅스 중심)
명령어 인터프리터나 셸을 통해 입력된 명령을 수행하려면 새로운 프로세스를 시작해야 하며, 이를 위해 시스템 호출이 필요하다. 리눅스 커널은 최고 권한 수준(x86, Ring 0)에서 실행되며 하드웨어 제어, 응용 프로그램 스케줄링 및 시분할 실행을 제어한다. 하드웨어 제어 권한은 커널에 있으므로, 응용 프로그램에서 파일 시스템 등을 직접 제어할 수 없다. 따라서 응용 프로그램이 하드웨어 데이터를 읽거나 쓰려면 커널의 장치 드라이버와 연동해야 하며, 결국 커널의 파일 시스템 드라이버를 통해야 하므로 시스템 호출 방식을 사용한다.[1]
6. 1. 유닉스 시스템
명령어 인터프리터 또는 셸을 통해 입력된 명령을 수행하는 과정에서 새로운 프로세스를 시작하기 위해서는 시스템 호출을 해야 한다. 예를 들어 유닉스 시스템에서는 새로운 프로세스를 시작하기 위해 exec 시스템 호출 이후 fork 시스템 호출이 뒤따른다. exec 호출은 호출 프로세스 위에 다른 실행가능한 새로운 프로세스를 띄우고, fork 호출은 현재 실행 중인 프로세스를 복제한다.[1]6. 2. 리눅스 커널
리눅스 커널은 최고 권한 수준(x86, Ring 0)에서 실행되며 하드웨어 제어, 응용 프로그램 스케줄링 및 시분할 실행을 제어한다.[1] 하드웨어 제어 권한은 커널이 가지고 있으므로, 파일 시스템과 같이 응용 프로그램에서 직접 제어할 수 없는 경우가 있다.[1] 따라서 응용 프로그램에서 하드웨어 데이터를 가져오거나 쓰려면 커널의 장치 드라이버와 연동되어 실행되어야 한다.[1] 즉, 응용 프로그램이 파일 시스템을 이용하려면 커널의 파일 시스템 드라이버를 통해야 하므로 시스템 호출을 사용한다.[1]커널의 장치 드라이버는 다음과 같은 구조로 장치를 정의하며, 각 함수는 응용 프로그램의 요청에 따라 커널에서 실행된다.[1]
```cpp
static struct file_operations hw_mydrv_fops = {
.owner = THIS_MODULE,
.open = mydrv_open,
.release = mydrv_release,
.read = mydrv_read,
.write = mydrv_write,
.unlocked_ioctl = mydrv_ioctl,
};
static struct miscdevice hw_mydrv_driver = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mydrv",
.fops = &hw_mydrv_fops,
};
```
응용 프로그램은 CPU 권한 수준이 사용자 공간에서 실행되므로 커널 공간이 필요한 하드웨어 제어를 할 수 없다.[1] 따라서 위와 같이 커널의 장치 드라이버를 통해 하드웨어를 제어한다.[1]
응용 프로그램 함수가 커널 함수와 연결되는 절차는 다음과 같다.[1]
1. 응용 프로그램에서 `open(...)` 함수를 실행하면, libc에서 제공하는 `open` 함수는 인수 데이터를 레지스터에 넣고 소프트웨어 인터럽트를 실행한다.
2. 소프트웨어 인터럽트에 의해 ISR(Interrupt Service Routine)이 있는 커널의 인터럽트 처리 위치를 찾아 해당 주소로 실행을 옮긴다. 이때 CPU는 최고 권한 수준의 실행 모드가 되며, 이는 CPU의 인터럽트 메커니즘에 의해 자동 변환된다.
3. 시스템 호출 함수 중 해당 인터럽트 벡터 숫자를 이용하여 함수의 위치를 찾고 해당 주소로 점프한다. 커널 속 장치 목록에서 해당 장치를 찾고 드라이버 함수 '''`.open`'''에 정의된 '''`mydrv_open(...)`'''이 호출된다.
4. `mydrv_open()` 함수의 '''`return`'''에 따라 커널 함수 호출이 완료되고, 커널은 다시 해당 응용 프로그램을 스케줄링하여 활성화하고 해당 프로세서로 전환한다. 이때 CPU 권한 수준은 다시 사용자 모드로 전환된다.
`write` 함수나 `read` 함수 호출에 의해 커널 함수가 호출된 후 `return`으로 종료되지 않으면, 응용 프로그램은 스케줄링에서 빠져 커널 상태에 머무르며 블럭킹 현상이 발생한다.[1] 이 블럭킹은 해당 드라이버의 인터럽트 등으로 해제할 수 있다.[1]
6. 3. 리눅스 커널의 장치 드라이버와 시스템 호출 예
리눅스 커널은 최고 권한 수준(x86, Ring 0)에서 실행되며, 하드웨어 제어, 응용 프로그램 스케줄링 및 시분할 실행을 제어한다. 하드웨어 제어 권한은 모두 커널에 있으므로, 응용 프로그램은 파일 시스템 등을 직접 제어할 수 없다. 따라서 응용 프로그램이 하드웨어 데이터를 읽거나 쓰려면 커널의 장치 드라이버와 연동해야 한다. 결국 응용 프로그램이 파일 시스템을 이용하려면 커널의 파일 시스템 드라이버를 통해야 하므로, 시스템 호출 방식을 사용한다.- 리눅스 커널의 장치 드라이버 예
: 권한 수준 커널 공간(kernel space, x86 레벨 링 0, ARM 슈퍼바이저 모드(Supervisor Mode))에서 실행.
static struct file_operations hw_mydrv_fops = {
.owner = THIS_MODULE,
.open = mydrv_open,
.release = mydrv_release,
.read = mydrv_read,
.write = mydrv_write,
.unlocked_ioctl = mydrv_ioctl,
};
static struct miscdevice hw_mydrv_driver = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mydrv",
.fops = &hw_mydrv_fops,
};
위와 같은 구조로 장치를 정의하며, 각 함수는 응용 프로그램의 요청에 따라 커널에서 실행된다.
- 응용 프로그램 예
: 권한 수준 사용자 공간(user space, x86 링 3, ARM 사용자 모드(User Mode))에서 실행.
: 응용 프로그램에서 리눅스 커널의 장치 드라이버 호출.
char gbuff[100];
int main(int argc, char*argv[])
{
int leng;
int fp;
fp = open("/dev/mydrv", ....); // mydrv_open 호출
gbuff[0] = 10;
gbuff[1] = 20;
leng = 2;
write(fp, (void*) gbuff, leng); // mydrv_write 호출
close(fp); // mydrv_release 호출
return 0;
}
응용 프로그램은 CPU 권한 수준이 사용자 공간에서 실행되므로, 커널 공간이 필요한 하드웨어 제어는 위와 같이 커널 장치를 통해 이루어진다.
응용 프로그램 함수가 커널 함수와 연결되는 절차는 다음과 같다.
1. 응용 프로그램에서 open() 함수를 실행하면, libc에서 제공하는 open 함수 내에서 인수 데이터를 레지스터에 넣고 소프트웨어 인터럽트를 실행한다.
2. 소프트웨어 인터럽트에 의해 ISR(Interrupt Service Routine)이 있는 커널의 인터럽트 처리 위치를 찾아 해당 주소로 실행을 옮긴다. 이때 CPU는 최고 권한 수준의 실행 모드가 되며, 이는 CPU 인터럽트 메커니즘에 의해 자동 변환된다.
3. 여러 시스템 호출 함수 중 해당 인터럽트 벡터 숫자를 이용해 함수 위치를 찾고 해당 주소로 점프한다. 커널 내 장치 목록에서 해당 장치를 찾고, 드라이버의 '.open'에 정의된 'mydrv_open()'이 호출된다.
4. mydrv_open() 함수의 'return'에 따라 커널 함수 호출이 완료되고, 커널은 다시 해당 응용 프로그램을 스케줄링하여 활성화하고 해당 프로세서로 전환한다. 이때 CPU 권한 수준은 다시 사용자 모드로 전환된다.
write 함수나 read 함수 호출 후 커널 함수가 return으로 종료되지 않으면, 응용 프로그램은 스케줄링에서 제외되어 커널 상태에 머무르며 블로킹 현상이 발생한다. 이는 해당 드라이버의 인터럽트 등을 통해 해제할 수 있다.
7. 시스템 호출의 예시 및 도구
유닉스, 유닉스 계열 운영체제 및 기타 POSIX 호환 운영체제에서 널리 사용되는 시스템 호출은 open
, read
, write
, close
, wait
, exec
, fork
, exit
및 kill
등이 있다. 많은 현대 운영 체제는 수백 개의 시스템 호출을 가지고 있다. 예를 들어, 리눅스와 OpenBSD는 각각 300개 이상의 서로 다른 호출을 가지며,[2][3] NetBSD는 500개에 가깝고,[4] FreeBSD는 500개 이상을 가지고 있으며,[5] 윈도우는 win32k(그래픽)와 ntdll(코어) 시스템 호출로 나뉘어 약 2000개에 가깝고,[6] 플랜 9는 51개를 가지고 있다.[7]
strace, ftrace 및 truss와 같은 도구를 사용하면 프로세스가 시작부터 실행되어 해당 프로세스가 호출하는 모든 시스템 호출을 보고하거나, 이미 실행 중인 프로세스에 연결하여 해당 프로세스가 수행하는 모든 시스템 호출을 가로챌 수 있다 (단, 작업이 사용자의 권한을 침해하지 않는 경우). 이 프로그램의 특별한 기능은 일반적으로 ptrace와 같은 시스템 호출이나 procfs의 파일에 대한 시스템 호출로 구현된다.
8. 프로세서 모드와 컨텍스트 스위칭
대부분의 유닉스 계열 시스템에서 시스템 호출은 커널 모드에서 처리된다. 이는 프로세서 실행 모드를 더 높은 권한으로 변경하여 수행되지만, ''프로세스'' 컨텍스트 스위칭은 필요하지 않으며, ''권한'' 컨텍스트 스위칭이 발생한다. 하드웨어는 프로세서 상태 레지스터에 따라 실행 모드를 구분하며, 프로세스는 운영 체제에서 제공하는 추상화 개념이다. 시스템 호출은 일반적으로 다른 프로세스로의 컨텍스트 스위칭을 발생시키지 않고, 호출한 프로세스의 컨텍스트 내에서 처리된다.
8. 1. 멀티스레딩 환경에서의 시스템 호출 처리 모델
멀티스레딩 (컴퓨터 아키텍처) 프로세스에서는 여러 스레드 (컴퓨팅)에서 시스템 호출을 할 수 있다. 이러한 호출의 처리는 특정 운영 체제 커널과 애플리케이션 런타임 환경의 설계에 따라 달라진다. 다음은 운영 체제에서 따르는 일반적인 모델이다.- '''Many-to-one''' 모델: 프로세스의 모든 사용자 스레드에서 발생하는 모든 시스템 호출은 단일 커널 레벨 스레드에 의해 처리된다. 이 모델은 차단 시스템 호출(사용자의 입력을 기다리는 등)이 다른 모든 스레드를 정지시킬 수 있고, 한 번에 하나의 스레드만 커널에 접근할 수 있으므로 프로세서의 여러 코어를 활용할 수 없다는 심각한 단점이 있다.
- '''One-to-one''' 모델: 모든 사용자 스레드는 시스템 호출 중에 별도의 커널 레벨 스레드에 연결된다. 이 모델은 위에서 언급한 차단 시스템 호출 문제를 해결한다. 이는 모든 주요 리눅스 배포판, macOS, iOS, 최신 Windows 및 Solaris 버전에서 찾아볼 수 있다.
- '''Many-to-many''' 모델: 이 모델에서는 사용자 스레드 풀이 커널 스레드 풀에 매핑된다. 사용자 스레드 풀의 모든 시스템 호출은 해당 커널 스레드 풀의 스레드에 의해 처리된다.
- '''하이브리드''' 모델: 이 모델은 커널에서 선택한 옵션에 따라 many-to-many 및 one-to-one 모델을 모두 구현한다. 이는 이전 버전의 IRIX, HP-UX 및 Solaris에서 발견된다.
참조
[1]
manual
IBM System/360 Operating System System Programmer's Guide
http://bitsavers.org[...]
1967-03-00
[2]
웹사이트
syscalls(2) - Linux manual page
http://man7.org/linu[...]
[3]
웹사이트
System call names (kern/syscalls.c)
http://bxr.su/OpenBS[...]
2013-09-14
[4]
웹사이트
System call names (kern/syscalls.c)
http://bxr.su/NetBSD[...]
2013-10-17
[5]
웹사이트
FreeBSD syscalls.c, the list of syscall names and IDs
http://fxr.watson.or[...]
[6]
웹사이트
Windows WIN32K.SYS System Call Table (NT/2000/XP/2003/Vista/2008/7/8/10)
http://j00ru.vexilli[...]
2017-11-05
[7]
웹사이트
sys.h
http://9p.io/sources[...]
[8]
웹사이트
SYSENTER
http://wiki.osdev.or[...]
[9]
웹사이트
Linux 2.5 gets vsyscalls, sysenter support
http://kerneltrap.or[...]
2002-12-19
[10]
웹사이트
Sysenter Based System Call Mechanism in Linux 2.6
http://articles.manu[...]
2006-00-00
[11]
웹사이트
Liberation: x86 Instruction Set Reference
http://x86.renejesch[...]
[12]
서적
Operating System Concepts
Wiley
[13]
서적
The Design of the UNIX Operating System
Prentice Hall
1986-00-00
[14]
웹사이트
Discussion of system call implementation at ProgClub including quote from Bach 1986
http://www.progclub.[...]
[15]
웹사이트
Threads
http://www.cs.uic.ed[...]
[16]
웹사이트
Threading Models
http://kevinmarquet.[...]
[17]
웹사이트
システムコール(スーパーバイザコール / SVC)とは - IT用語辞典 e-Words
https://e-words.jp/w[...]
[18]
웹사이트
2.7.6 演習: ユーザーによって実行されるプロセスのトレース
https://docs.oracle.[...]
Oracle
2024-11-00
[19]
서적
例題で学ぶLinuxプログラミング
ピアソン・エデュケーション
2001-05-20
[20]
서적
オペレーティングシステムの概念
共立出版
2010-11-00
[21]
웹사이트
Linux Systemcall Reference
http://syscalls.kern[...]
[22]
웹사이트
linux/include/uapi/asm-generic/unistd.h - torvalds/linux - GitHub
https://github.com/t[...]
[23]
웹사이트
FreeBSD syscalls.c, the list of syscall names and IDs
http://fxr.watson.or[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com